package check

import (
	"gitee.com/u-language/u-language/ucom/ast"
	"gitee.com/u-language/u-language/ucom/astdata"
	"gitee.com/u-language/u-language/ucom/enum"
	"gitee.com/u-language/u-language/ucom/errcode"
	"gitee.com/u-language/u-language/ucom/internal/utils"
)

// checkCall 检查call节点类型是否匹配
//   - ptr是被检查的call节点，不能为nil
//   - sbt是被检查的赋值节点所属代码块的符号表，不能为nil
//   - t是被检查节点所属的抽象语法树
//   - InAutoFree指示是否在自动释放块内
func checkCall(ptr *ast.CallNode, sbt *ast.Sbt, t *ast.Tree, InAutoFree bool) (code errcode.ErrCode, msg errcode.Msg, ret []astdata.RetValue) {
	defer check_recover(&code, &msg)
	var calleeinfo *ast.FuncInfo
	var ok bool
	code, msg, ok, ret = predefined_func(ptr, sbt, t, InAutoFree)
	if ok { //如果是预定义的函数或视为函数调用的类型转换
		return code, msg, ret
	}
	parameoff := 0
	switch ptr.FuncName.(type) {
	case *ast.Object, *ast.GenericInstantiation:
		info := sbt.Have(ptr.FuncName.FuncName())
		if info.Kind == enum.SymbolFunc { //如果是调用函数
			calleeinfo = info.PtrFuncInfo()
		} else { //如果调用的不是函数
			return errcode.CallNoFunc, errcode.NewMsgSymbol(ptr.FuncName.FuncName()), nil
		}
	case *ast.Objects:
		n := ptr.FuncName.(*ast.Objects)
		var isImport bool
		code, msg, _, _, calleeinfo, isImport = check_selector(n, sbt)
		if code != errcode.NoErr {
			return code, msg, nil
		}
		if calleeinfo == nil {
			return errcode.CallNoFunc, errcode.NewMsgSymbol(ptr.FuncName.FuncName()), nil
		}
		if !isImport { //如果不是导入符号，也就是方法调用
			parameoff = 1
			tmp := *calleeinfo
			calleeinfo = &tmp
			calleeinfo.Parame = calleeinfo.Parame[1:]
		}
	}
	plen := len(ptr.Parame)
	//记录在自动释放块内被调用的函数
	_, ok = ast.Builtin_func_info[ptr.FuncName.FuncName()]
	if !ok && InAutoFree {
		parameoff++
		if o, ok := ptr.FuncName.(*ast.Object); ok {
			t.InAutoFreeFuncCallName.Push(o)
		} else {
			o := ptr.FuncName.(*ast.Objects)
			t.InAutoFreeFuncCallName.Push(o)
		}
		code, msg = check_parame_num(ptr.FuncName.FuncName(), plen, len(calleeinfo.Parame)+1)
		if code != errcode.NoErr {
			return code, msg, nil
		}
	} else {
		code, msg = check_parame_num(ptr.FuncName.FuncName(), plen, len(calleeinfo.Parame))
		if code != errcode.NoErr {
			return code, msg, nil
		}
	}
	for i := parameoff; i < plen; i++ {
		code, msg, typ := ret_type_str(ptr.Parame[i], sbt, t, InAutoFree) //获取传递的参数类型
		if code != errcode.NoErr {
			return code, msg, nil
		}
		calleeoff := i
		if InAutoFree {
			calleeoff--
		}
		if !utils.Typ_is_equal(calleeinfo.Parame[calleeoff].Type.Typ(), typ) { //如果类型不相等
			return errcode.TypeIsNotEqual, errcode.NewMsgCallTypeIsNotEqual(calleeinfo.Parame[calleeoff].Type.Typ(), typ, i+1), nil
		}
	}
	return errcode.NoErr, nil, calleeinfo.RetValue
}

// predefined_func 检查预定义的函数或视为函数调用的类型转换
func predefined_func(ptr *ast.CallNode, sbt *ast.Sbt, t *ast.Tree, InAutoFree bool) (code errcode.ErrCode, msg errcode.Msg, ok bool, ret []astdata.RetValue) {
	plen := len(ptr.Parame)
	switch ptr.FuncName.FuncName() {
	case enum.Printf:
		if plen == 0 {
			return errcode.InsuParame, nil, true, nil
		}
		var typ string
		code, msg, typ = ret_type_str(ptr.Parame[0], sbt, t, InAutoFree)
		if code != errcode.NoErr {
			return code, msg, true, nil
		}
		if typ != enum.String {
			return errcode.FirstParameOfPrinfASting, nil, true, nil
		}
		code, msg = chech_All_Parame(ptr.Parame, sbt, t, InAutoFree)
		return code, msg, true, nil
	case enum.Int: //是类型转换
		code, msg = check_type_Conversion(ptr, plen, sbt, t, InAutoFree)
		return code, msg, true, retInt
	case enum.Float: //是类型转换
		code, msg = check_type_Conversion(ptr, plen, sbt, t, InAutoFree)
		return code, msg, true, retFloat
	case enum.Malloc:
		code, msg = check_parame_num(enum.Malloc, plen, 1)
		if code != errcode.NoErr {
			return code, msg, true, nil
		}
		var typ string
		//检查是不是类型，不是会panic,checkCall会处理
		if o, ok := ptr.Parame[0].(*ast.Object); ok {
			sbt.HaveType(o.Name)
			typ = o.Name
		} else if o, ok := ptr.Parame[0].(*ast.Objects); ok {
			sbt.HaveType(utils.GeneratePackageSymbol(o.Slice[0].Name, o.Slice[1].Name))
			typ = o.Typ()
		} else {
			return errcode.ParameOfMallocAType, nil, true, nil
		}
		return errcode.NoErr, nil, true, []astdata.RetValue{astdata.NewNameAndType("", ast.NewObject(ast.TypeObj, "&"+typ))}
	case enum.Free, enum.MemPoolFree:
		code, msg = check_parame_num(ptr.FuncName.FuncName(), plen, 1)
		if code != errcode.NoErr {
			return code, msg, true, nil
		}
		return errcode.NoErr, nil, true, nil
	case enum.UnsafeAdd:
		code, msg = check_parame_num(enum.UnsafeAdd, plen, 2)
		if code != errcode.NoErr {
			return code, msg, true, nil
		}
		//检查第二个参数类型是不是整数
		var typ string
		code, msg, typ = ret_type_str(ptr.Parame[1], sbt, t, InAutoFree)
		if code != errcode.NoErr {
			return code, msg, true, nil
		}
		if typ != enum.Int {
			return errcode.SecondParameOfUnsafeAddAnInt, nil, true, nil
		}
		//检查第一个参数类型是不是指针类型
		code, msg, typ = ret_type_str(ptr.Parame[0], sbt, t, InAutoFree)
		if code != errcode.NoErr {
			return code, msg, true, nil
		}
		if !utils.IsPtr(typ) {
			return errcode.FirstParameOfUnsafeAddAPtr, nil, true, nil
		}
		return errcode.NoErr, nil, true, retUnsafePointer
	case enum.UnsafeConvert:
		code, msg = check_parame_num(enum.UnsafeConvert, plen, 2)
		if code != errcode.NoErr {
			return code, msg, true, nil
		}
		//检查第一个参数类型是不是指针类型
		var typ string
		code, msg, typ = ret_type_str(ptr.Parame[0], sbt, t, InAutoFree)
		if code != errcode.NoErr {
			return code, msg, true, nil
		}
		if !utils.IsPtr(typ) {
			return errcode.FirstParameOfUnsafeConvertAPtr, nil, true, nil
		}
		//检查第二个参数是不是指针类型
		code, msg, typ = ret_type_str(ptr.Parame[1], sbt, t, InAutoFree)
		if code != errcode.NoErr {
			return code, msg, true, nil
		}
		if !utils.IsPtr(typ) {
			return errcode.SecondParameOfUnsafeConvertAPtrType, nil, true, nil
		}
		return errcode.NoErr, nil, true, []astdata.RetValue{astdata.NewNameAndType("ptr", ast.NewObject(ast.TypeObj, typ))}
	case enum.UnsafeSizeof:
		code, msg = check_parame_num(enum.UnsafeSizeof, plen, 1)
		if code != errcode.NoErr {
			return code, msg, true, nil
		}
		//检查第一个参数是不是类型
		if o, ok := ptr.Parame[0].(*ast.Object); ok { //如果是对象类型
			sbt.HaveType(o.Name) //检查是不是类型
		} else if o, ok := ptr.Parame[0].(*ast.Objects); ok { //如果是选择器
			_, _, _, _, _, isImport := check_selector(o, sbt)
			if !isImport || len(o.Slice) != 2 { //如果不是导入符号或者选择器不是只有一个.
				return errcode.FirstParameOfUnsafeSizeAType, nil, true, nil
			}
			info := sbt.Have(utils.GeneratePackageSymbol(o.Slice[0].Name, o.Slice[1].Name))
			if utils.IsNoType(info.Kind) { //如果不是类型
				return errcode.FirstParameOfUnsafeSizeAType, nil, true, nil
			}
		} else {
			return errcode.FirstParameOfUnsafeSizeAType, nil, true, nil
		}
		return errcode.NoErr, nil, true, retInt
	case enum.MallocSize:
		code, msg = check_parame_num(enum.MallocSize, plen, 1)
		if code != errcode.NoErr { //如果传参数量不对
			return code, msg, true, nil
		}
		var typ string
		//检查第一个参数是不是整数
		code, msg, typ = ret_type_str(ptr.Parame[0], sbt, t, InAutoFree)
		if code != errcode.NoErr {
			return code, msg, true, nil
		}
		if typ != enum.Int {
			return errcode.ParameForMallocSizeMustInt, nil, true, nil
		}
		return errcode.NoErr, nil, true, retUnsafePointer
	case enum.UnsafePointer:
		code, msg = check_parame_num(enum.UnsafePointer, plen, 1)
		if code != errcode.NoErr { //如果传参数量不对
			return code, msg, true, nil
		}
		//检查第一个参数是不是指针
		var typ string
		code, msg, typ = ret_type_str(ptr.Parame[0], sbt, t, InAutoFree)
		if code != errcode.NoErr {
			return code, msg, true, nil
		}
		if !utils.IsPtr(typ) {
			return errcode.CanOnlyConvertPointerTypeToUnsafePointer, nil, true, nil
		}
		return errcode.NoErr, nil, true, retUnsafePointer
	case enum.MemPoolNew:
		return errcode.NoErr, nil, true, retUnsafePointer
	}
	return errcode.NoErr, nil, false, nil
}

var (
	typInt           = ast.NewObject(ast.TypeObj, enum.Int)
	typFloat         = ast.NewObject(ast.TypeObj, enum.Float)
	typUnsafePointer = ast.NewObject(ast.TypeObj, enum.UnsafePointer)
)

var (
	retInt           = []astdata.RetValue{astdata.NewNameAndType("ret", typInt)}
	retFloat         = []astdata.RetValue{astdata.NewNameAndType("ret", typFloat)}
	retUnsafePointer = []astdata.RetValue{astdata.NewNameAndType("ptr", typUnsafePointer)}
)

// 检查参数数量
//   - callernum 传递的参数数量
//   - calleenum 函数声明的参数数量
func check_parame_num(funcname string, callernum int, calleenum int) (code errcode.ErrCode, msg errcode.Msg) {
	if callernum > calleenum { //如果传递的参数数量大于函数声明的参数数量
		return errcode.TManyParame, errcode.NewMsgNumberOfParameNoMatch(callernum, calleenum, funcname)
	} else if callernum < calleenum { //如果传递的参数数量小于函数声明的参数数量
		return errcode.InsuParame, errcode.NewMsgNumberOfParameNoMatch(callernum, calleenum, funcname)
	}
	return errcode.NoErr, nil
}

// chech_All_Parame 检查所有参数
func chech_All_Parame(parame []ast.Expr, sbt *ast.Sbt, t *ast.Tree, InAutoFree bool) (code errcode.ErrCode, msg errcode.Msg) {
	//TODO:支持输出所有错误
	for _, v := range parame {
		code2, msg2, _ := ret_type_str(v, sbt, t, InAutoFree)
		if code2 != errcode.NoErr {
			code, msg = code2, msg2
			return
		}
	}
	return
}

// check_type_Conversion 检查类型转换
func check_type_Conversion(ptr *ast.CallNode, plen int, sbt *ast.Sbt, t *ast.Tree, InAutoFree bool) (code errcode.ErrCode, msg errcode.Msg) {
	code, msg = check_parame_num(ptr.FuncName.FuncName(), plen, 1)
	if code != errcode.NoErr {
		return code, msg
	}
	code, msg, typ := ret_type_str(ptr.Parame[0], sbt, t, InAutoFree)
	if code != errcode.NoErr {
		return code, msg
	}
	if typ[0] == '&' { //如果被转换的是指针
		return errcode.CanOnlyConvertPointerTypeToUnsafePointer, nil
	}
	switch ptr.FuncName.FuncName() {
	case enum.Int:
		if typ != enum.Float { //如果转换非float类型为int
			if o, ok := ptr.Parame[0].(*ast.Object); ok && o.Kind == ast.INTOBJ {
				break
			}
			return errcode.CanOnlyConvertFloatToInt, nil
		}
	case enum.Float:
		if typ != enum.Int { //如果转换非int类型为float
			return errcode.CanOnlyConvertIntToFloat, nil
		}
	}
	return errcode.NoErr, nil
}
